<!-- Licensed under a BSD license. See license.html for license -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
    <title>WebGL Text - Textures - different colors</title>
<link type="text/css" href="resources/webgl-tutorials.css" rel="stylesheet" />
</head>
<body>
<canvas id="canvas"></canvas>
</body>
<!--
This sample uses TWGL (Tiny WebGL) to hide the clutter.
Otherwise the sample would be full of code not related to the point of the sample.
For more info see https://webgl2fundamentals.org/webgl/lessons/webgl-less-code-more-fun.html
-->
<script src="resources/twgl-full.min.js"></script>
<script src="resources/m4.js"></script>
<script>
"use strict";

var fVertexShaderSource = `#version 300 es

// an attribute is an input (in) to a vertex shader.
// It will receive data from a buffer
in vec4 a_position;
in vec4 a_color;

// A matrix to transform the positions by
uniform mat4 u_matrix;

// a varying the color to the fragment shader
out vec4 v_color;

// all shaders have a main function
void main() {
  // Multiply the position by the matrix.
  gl_Position = u_matrix * a_position;

  // Pass the color to the fragment shader.
  v_color = a_color;
}
`;

var fFragmentShaderSource = `#version 300 es

precision highp float;

// the varied color passed from the vertex shader
in vec4 v_color;

// we need to declare an output for the fragment shader
out vec4 outColor;

void main() {
  outColor = v_color;
}
`;

var textVertexShaderSource = `#version 300 es
in vec4 a_position;
in vec2 a_texcoord;

uniform mat4 u_matrix;

out vec2 v_texcoord;

void main() {
  // Multiply the position by the matrix.
  gl_Position = u_matrix * a_position;

  // Pass the texcoord to the fragment shader.
  v_texcoord = a_texcoord;
}
`;

var textFragmentShaderSource = `#version 300 es
precision highp float;

// Passed in from the vertex shader.
in vec2 v_texcoord;

uniform sampler2D u_texture;
uniform vec4 u_color;

out vec4 outColor;

void main() {
   outColor = texture(u_texture, v_texcoord) * u_color;
}
`;

// make a 2d canvas for making text textures.
var textCtx = document.createElement("canvas").getContext("2d");

// Puts text in center of canvas.
function makeTextCanvas(text, width, height) {
  textCtx.canvas.width  = width;
  textCtx.canvas.height = height;
  textCtx.font = "20px monospace";
  textCtx.textAlign = "center";
  textCtx.textBaseline = "middle";
  textCtx.fillStyle = "white";
  textCtx.clearRect(0, 0, textCtx.canvas.width, textCtx.canvas.height);
  textCtx.fillText(text, width / 2, height / 2);
  return textCtx.canvas;
}

function main() {
  // Get A WebGL context
  /** @type {HTMLCanvasElement} */
  var canvas = document.querySelector("#canvas");
  var gl = canvas.getContext("webgl2");
  if (!gl) {
    return;
  }

  // tell twgl to match program to a_program and
  // normal to a_normal etc...
  twgl.setAttributePrefix("a_");

  // setup GLSL programs
  var fProgramInfo = twgl.createProgramInfo(
      gl, [fVertexShaderSource, fFragmentShaderSource]);
  var textProgramInfo = twgl.createProgramInfo(
      gl, [textVertexShaderSource, textFragmentShaderSource]);

  // Create data for 'F'
  var fBufferInfo = twgl.primitives.create3DFBufferInfo(gl);
  var fVAO = twgl.createVAOFromBufferInfo(
      gl, fProgramInfo, fBufferInfo);

  // Create a unit quad for the 'text'
  var textBufferInfo = twgl.primitives.createXYQuadBufferInfo(gl, 1);
  var textVAO = twgl.createVAOFromBufferInfo(
      gl, textProgramInfo, textBufferInfo);

  // colors, 1 for each F
  var colors = [
    [0.0, 0.0, 0.0, 1], // 0
    [1.0, 0.0, 0.0, 1], // 1
    [0.0, 1.0, 0.0, 1], // 2
    [1.0, 1.0, 0.0, 1], // 3
    [0.0, 0.0, 1.0, 1], // 4
    [1.0, 0.0, 1.0, 1], // 5
    [0.0, 1.0, 1.0, 1], // 6
    [0.5, 0.5, 0.5, 1], // 7
    [0.5, 0.0, 0.0, 1], // 8
    [0.0, 0.0, 0.0, 1], // 9
    [0.5, 5.0, 0.0, 1], // 10
    [0.0, 5.0, 0.0, 1], // 11
    [0.5, 0.0, 5.0, 1], // 12,
    [0.0, 0.0, 5.0, 1], // 13,
    [0.5, 5.0, 5.0, 1], // 14,
    [0.0, 5.0, 5.0, 1], // 15,
  ];

  // create text textures, one for each F
  var textTextures = [
    "anna",   // 0
    "colin",  // 1
    "james",  // 2
    "danny",  // 3
    "kalin",  // 4
    "hiro",   // 5
    "eddie",  // 6
    "shu",    // 7
    "brian",  // 8
    "tami",   // 9
    "rick",   // 10
    "gene",   // 11
    "natalie",// 12,
    "evan",   // 13,
    "sakura", // 14,
    "kai",    // 15,
  ].map(function(name) {
    var textCanvas = makeTextCanvas(name, 100, 26);
    var textWidth  = textCanvas.width;
    var textHeight = textCanvas.height;
    var textTex = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, textTex);
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
    gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, textCanvas);
    gl.generateMipmap(gl.TEXTURE_2D);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
    return {
      texture: textTex,
      width: textWidth,
      height: textHeight,
    };
  });

  var fUniforms = {
    u_matrix: m4.identity(),
  };

  var textUniforms = {
    u_matrix: m4.identity(),
//    u_texture: textTex,
  };

  function degToRad(d) {
    return d * Math.PI / 180;
  }

  var translation = [0, 30, 0];
  var rotation = [degToRad(190), degToRad(0), degToRad(0)];
  var scale = [1, 1, 1];
  var fieldOfViewRadians = degToRad(60);
  var rotationSpeed = 1.2;

  var then = 0;

  requestAnimationFrame(drawScene);

  function drawScene(time) {
    // Convert to seconds
    var now = time * 0.001;
    // Subtract the previous time from the current time
    var deltaTime = now - then;
    // Remember the current time for the next frame.
    then = now;

    twgl.resizeCanvasToDisplaySize(gl.canvas);

    // Every frame increase the rotation a little.
    rotation[1] += rotationSpeed * deltaTime;

    // Tell WebGL how to convert from clip space to pixels
    gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

    // Clear the canvas
    gl.clearColor(0, 0, 0, 0);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

    // turn on depth testing
    gl.enable(gl.DEPTH_TEST);

    // tell webgl to cull faces
    gl.enable(gl.CULL_FACE);

    gl.disable(gl.BLEND);
    gl.depthMask(true);

    // Compute the matrix
    var aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
    var zNear = 1;
    var zFar = 2000;
    var projectionMatrix = m4.perspective(fieldOfViewRadians, aspect, zNear, zFar);

    // Compute the camera's matrix using look at.
    var cameraRadius = 360;
    var cameraPosition = [Math.cos(now) * cameraRadius, 0, Math.sin(now) * cameraRadius];
    var target = [0, 0, 0];
    var up = [0, 1, 0];
    var cameraMatrix = m4.lookAt(cameraPosition, target, up);
    var viewMatrix = m4.inverse(cameraMatrix);

    var textPositions = [];

    var spread = 170;
    for (var yy = -1; yy <= 1; ++yy) {
      for (var xx = -2; xx <= 2; ++xx) {
        var fViewMatrix = m4.translate(viewMatrix,
            translation[0] + xx * spread, translation[1] + yy * spread, translation[2]);
        fViewMatrix = m4.xRotate(fViewMatrix, rotation[0]);
        fViewMatrix = m4.yRotate(fViewMatrix, rotation[1] + yy * xx * 0.2);
        fViewMatrix = m4.zRotate(fViewMatrix, rotation[2] + now + (yy * 3 + xx) * 0.1);
        fViewMatrix = m4.scale(fViewMatrix, scale[0], scale[1], scale[2]);
        fViewMatrix = m4.translate(fViewMatrix, -50, -75, 0);

        // remember the position for the text
        textPositions.push([fViewMatrix[12], fViewMatrix[13], fViewMatrix[14]]);

        // setup to draw the 'F'
        gl.useProgram(fProgramInfo.program);

        // setup the attributes and buffers for the F
        gl.bindVertexArray(fVAO);

        fUniforms.u_matrix = m4.multiply(projectionMatrix, fViewMatrix);

        twgl.setUniforms(fProgramInfo, fUniforms);

        twgl.drawBufferInfo(gl, fBufferInfo);
      }
    }

    gl.enable(gl.BLEND);
    gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
    gl.depthMask(false);

    textPositions.forEach(function(pos, ndx) {
      // select a texture
      var tex = textTextures[ndx];

      // use just the view position of the 'F' for the text

      // because pos is in view space that means it's a vector from the eye to
      // some position. So translate along that vector back toward the eye some distance
      var fromEye = m4.normalize(pos);
      var amountToMoveTowardEye = 150;  // because the F is 150 units long
      var viewX = pos[0] - fromEye[0] * amountToMoveTowardEye;
      var viewY = pos[1] - fromEye[1] * amountToMoveTowardEye;
      var viewZ = pos[2] - fromEye[2] * amountToMoveTowardEye;
      var desiredTextScale = -1 / gl.canvas.height;  // 1x1 pixels
      var scale = viewZ * desiredTextScale;

      var textMatrix = m4.translate(projectionMatrix,
          viewX, viewY, viewZ);
      // scale the F to the size we need it.
      textMatrix = m4.scale(textMatrix, tex.width * scale, tex.height * scale, 1);

      // setup to draw the text.
      gl.useProgram(textProgramInfo.program);

      gl.bindVertexArray(textVAO);

      m4.copy(textMatrix, textUniforms.u_matrix);
      textUniforms.u_texture = tex.texture;
      textUniforms.u_color = colors[ndx];

      twgl.setUniforms(textProgramInfo, textUniforms);

      // Draw the text.
      twgl.drawBufferInfo(gl, textBufferInfo);
    });

    requestAnimationFrame(drawScene);
  }
}

main();
</script>
</html>


